package db.util;

import java.io.PrintWriter;
import java.util.Arrays;

public class Table {
	public Table(String name, Column[] columns) {
		if (name == null) {
			throw new IllegalArgumentException("name cannot be null");
		}
		if (columns == null) {
			throw new IllegalArgumentException("columns cannot be null");
		}
		this.name = name.toLowerCase();
		this.columns = columns;
	}
	private String name;
	private Column[] columns;
	private Index primaryKey;
	private Index[] indexes;
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("table " + name + "(\n");
		for (Column c : columns) {
			sb.append("  ").append(c).append('\n');
		}
		if (primaryKey != null) {
			sb.append("  PK:").append(primaryKey).append('\n');
		}
		if (indexes != null) {
			for (Index index : indexes) {
				sb.append("  NDX:").append(index).append('\n');
			}
		}
		sb.append(")\n");
		return sb.toString();
	}
	public Index createIndex(String name, boolean nonUnique, int[] cols) {
		return new Index(name, nonUnique, cols);
	}

	public String getName() {
		return name;
	}
	public int gteColumnIndex(String colName) {
		for (int i=0 ; i<columns.length ; i++) {
			if (columns[i].getName().equals(colName)) return i;
		}
		return -1;
	}
	public void setPrimaryKey(Index primKey) {
		primaryKey = primKey;
	}
	public void setIndexes(Index[] indexes) {
		this.indexes = indexes;
	}
	public boolean equals(Table b, StringTree os, ColumnComparator cmp) {
		boolean result = true;
		if (!name.equals(b.name)) {
			result = false;
			os.printf("Differing table names: A:%s - B:%s%n", name, b.name);
		}
		if (columns.length != b.columns.length){ 
			result = false;
			os.printf("Differing table columns:%nA:%s%nB:%s%n", this, b);
		} else {
			for (int i=0 ; i<columns.length ; i++) {
				if (cmp != null) {
					if (!cmp.equals(columns[i], b.columns[i], os)) {
						result = false;
					}
				} else {
					if (!columns[i].equals(b.columns[i], os)) {
						result = false;
					}
				}
			}
		}
		if (primaryKey == null) {
			if (b.primaryKey != null) {
				os.printf("A has no primary key but B has%n");
				result = false;
			}
		} else {
			if (b.primaryKey == null) {
				os.printf("B has no primary key but A has%n");
				result = false;
			} else {
				if (!primaryKey.equals(b.primaryKey, true)) {
					os.printf("Primary keys differ; A:%s B:%s%n", primaryKey, b.primaryKey);
					result = false;
				}
			}
		}
		if (indexes == null && b.indexes != null) {
			os.printf("A has no indexes but B has%n");
			result = false;
		} else {
			if (b.indexes == null) {
				os.printf("B has no indexes but A has%n");
				result = false;
			} else {
				if (indexes.length != b.indexes.length) {
					os.printf("A and B have different numbers of indexes: A:%s B:%s%n", 
							indexes.length, b.indexes.length);
					result = false;
				} else {
					for (int i=0 ; i<indexes.length ; i++) {
						if (!indexes[i].equals(b.indexes[i], false)) {
							os.printf("Indices differ; A:%s B:%s%n", indexes[i], b.indexes[i]);
							result = false;
						}
					}
				}
			}
		}
		return result;
	}
	public class Index {
		private String name;
		private boolean nonUnique;
		private int[] cols;
		
		public Index(String name, boolean nonUnique, int[] cols) {
			this.name = name;
			this.nonUnique = nonUnique;
			this.cols = cols;
		}

		@Override
		public String toString() {
			StringBuilder sb = new StringBuilder();
			sb.append(name).append('.').append(nonUnique ? "nU" : "U").append('(');
			String sep = "";
			for (int col : cols) {
				sb.append(sep).append(columns[col].getName());
				sep = ",";
			}
			sb.append(')');
			return sb.toString();
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + Arrays.hashCode(cols);
			result = prime * result + ((name == null) ? 0 : name.hashCode());
			result = prime * result + (nonUnique ? 1231 : 1237);
			return result;
		}

		public boolean equals(Index b, boolean ignoreName) {
			if (this == b)
				return true;
			if (b == null)
				return false;
			if (cols.length != b.cols.length) {
				return false;
			}
			for (int i=0 ; i<cols.length ; i++) {
				String nameA = getName(i); 
				String nameB = b.getName(i); 
				if (!nameA.equals(nameB)) {
					return false;
				}
			}
			if (!ignoreName) {
				if (name == null) {
					if (b.name != null)
						return false;
				} else if (!name.equals(b.name))
					return false;
			}
			if (nonUnique != b.nonUnique)
				return false;
			return true;
		}
		public String getName(int i) {
			return columns[cols[i]].getName();
		}
	}
}
